﻿using System;
using System.Diagnostics.CodeAnalysis;
using Rhino.Mocks;

namespace Mockery.Core
{
    /// <summary>
    /// Arrangement organizes the mocks and stubs for the test run
    /// </summary>
    public class Arrangement : IArrangement
    {
        private readonly MockRepository _mockRepository;
        private readonly ITypeCache _typeCache;

        /// <summary>
        /// Injected constructor accepting the Rhino repository
        /// </summary>
        /// <param name="mockRepository">the mock repository</param>
        /// <param name="typeCache">the cache of types used in the test</param>
        public Arrangement( MockRepository mockRepository, ITypeCache typeCache)
        {
            if( mockRepository == null) throw new ArgumentNullException("mockRepository");

            _mockRepository = mockRepository;
            _typeCache = typeCache;
        }


        #region IMockeryRepository Members

        /// <summary>
        /// Adds a strict mock to the underlying repository
        /// </summary>
        /// <typeparam name="TInterface">type being mocked</typeparam>
        /// <returns>mocked interface</returns>
        public TInterface AddStrictMock<TInterface>()
        {
            return AddMockedType(_mockRepository.StrictMock<TInterface>());
        }

        private TInterface AddMockedType<TInterface>(TInterface implementation)
        {
            _typeCache.AddMockedType(implementation);

            return implementation;
        }

        /// <summary>
        /// Adds a dynamic mock to the underlying repository
        /// </summary>
        /// <typeparam name="TInterface">type being mocked</typeparam>
        /// <returns>mocked interface</returns>
        [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
        public TInterface AddDynamicMock<TInterface>() where TInterface : class
        {
            return AddMockedType(_mockRepository.DynamicMock<TInterface>());
        }

        /// <summary>
        /// Adds a stub to the underlying repository
        /// </summary>
        /// <typeparam name="TInterface">type being stubbed</typeparam>
        /// <returns>stubbed interface</returns>
        public TInterface AddStub<TInterface>()
        {
            return AddMockedType(_mockRepository.Stub<TInterface>());
        }

        /// <summary>
        /// Adds a mocked type to the arrangement, useful for manual mocks
        /// </summary>
        /// <typeparam name="TInterface">mocked instance type</typeparam>
        /// <param name="instance">instance value to be resolved when requested</param>
        void IArrangement.AddMockedType<TInterface>(TInterface instance)
        {
            _typeCache.AddMockedType(instance);
        }

        /// <summary>
        /// Retrieves the mocked instance associated with the given type
        /// </summary>
        /// <typeparam name="TInterface">mocked instance type</typeparam>
        /// <returns>mock instance in the arrangement</returns>
        public TInterface GetMockedType<TInterface>()
        {
            return _typeCache.GetMockedType<TInterface>();
        }

        #endregion
    }
}
